注意:所有文章除特别说明外,转载请注明出处.
[TOC]
第二章 对象及变量的并发访问
2.1 synchronize 同步方法
2.1.1 方法内的变量是线程安全
提示:方法中变量不存在非线程安全问题,永远都是线程安全的。这是方法内部变量的私有属性造成的。
2.1.2 实例变量非线程安全
2.1.6 synchronize锁重入
关键字 synchronize 拥有锁重入的功能,也就是在使用 synchronize 时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。
那么表示一个 synchronize 方法/块内部调用本类的其它 synchronize 方法/块时,是永远可以得到锁的。
提示:可重入锁是指:自己可以再次获取自己的内部锁。可重入锁也支持父子类继承的环境。
2.1.7 将任意对象作为对象监视器
多个线程调用同一个对象中的不同名称的 synchronize 同步方法或 synchronize(this) 同步代码块时,调用的效果就是按照顺序执行的,也就是同步的,阻塞的。
Java还支持任意对象作为对象监视器来实现同步功能,这个任意对象大多数是实例变量及方法参数,使用格式是:synchronize(非this对象)。
锁非this对象具有的优点:如果在一个类中很多个synchronize方法,这时虽然能实现同步,但是会受到阻塞,所以影响运行效率。但是如果使用同步代码块锁非this对象,则 synchronize(非this对象) 代码块中程序与同步方法是异步的,不与其他锁 this同步方法争抢 this锁,可提高效率。
提示:使用 synchronize(非this对象)同步块格式进行同步操作时,对象监视器必须是同一个对象,如果不是同一个对象监视器,运行的结果就是异步调用了,会交叉运行。
2.1.8 3个结论
1.当多个线程同时执行 synchronize(x){} 同步代码块时呈现同步效果。
同步的原因是因为使用了同一个 对象监视器,如果使用不同的对象监视器会异步输出。
2.当其它线程执行x对象中 synchronize 同步方法时呈现同步效果。
3.当其它线程执行x对象方法里面的 synchronize(this) 代码块时也呈现同步效果。
注意:如果其它线程调用不加 synchronize 关键字的方法时,还是异步调用。
2.1.9 静态同步 synchronize 方法与 synchronize(class) 代码块
在静态方法上加关键字 synchronize 实现的效果和非static方法效果一样。但在本质上是不同的,synchronize 关键字加到static静态方法上是给Class类上锁,而synchronize 关键字加到非static方法上是给对象上锁。
2.1.10 同步方法容易造成 synchronize 方法无限等待
对于这样的方法,提供多个锁解决问题,如果一个synchronize方法内部陷入死循环状态,只有该方法进入死锁状态,而其它被 synchronize关键字修饰的方法不会进入死锁状态,获取它们方法的锁正常执行。
2.1.11 多线程的死锁
Java线程死锁是一个经典的问题,因为不同的线程都在等在根本不可能被释放的锁,从而导致所有的任务都无法继续完成。
2.1.16 锁对象的改变
在将任何数据类型作为同步锁时,需要注意的是,是否有多个线程同时持有锁对象,如果同时持有相同的锁对象,则这些线程之间是同步的。如果分别获得锁对象,则这些线程是异步的。
2.2 volatile关键字
2.2.1 volatile关键字和synchronize关键字的比较
1.关键字 volatile 是线程同步的轻量级实现,所以volatile性能肯定比 synchronize 关键字好,并且volatile只能修饰变量,而synchronize关键字可以修饰方法,以及代码块。
2.多线程访问volatile不会发生阻塞,而synchronize会发生阻塞。
3.volatile能够保证数据的可见性,但是不能保证原子性。synchronize可以保证原子性,也可以间接保证可见性。
4.关键字 volatile 解决的是变量在多线程之间的可见性,而synchronize关键字解决的是多个线程之间访问资源的同步性。
提示:线程安全包括原子性和可见性两个方面,Java同步机制围绕着两方面保证线程安全的。